package com.sdgakj.utils.redissession.session;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.sdgakj.utils.redissession.service.ShiroSessionRepository;

@Component
public class CachingShiroSessionDao extends CachingSessionDAO
{
    
    private static final Logger logger = LoggerFactory.getLogger(CachingShiroSessionDao.class);
    
    private ShiroSessionRepository sessionRepository;
    
    /**
     * 重写CachingSessionDAO中readSession方法，如果Session中没有登陆信息就调用doReadSession方法从Redis中重读 session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null 代表没有登录，登录后Shiro会放入该值
     */
    @Override
    public Session readSession(Serializable sessionId) throws UnknownSessionException
    {
        Session cached = null;
        try
        {
            cached = super.getCachedSession(sessionId);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        // 如果缓存不存在或者缓存中没有登陆认证后记录的信息就重新从Redis中读取
        if(cached == null || cached.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null)
        {
            try
            {
                cached = this.doReadSession(sessionId);
                if(cached == null)
                {
                    throw new UnknownSessionException();
                }
                else
                {
                    // 重置Redis中缓存过期时间并缓存起来 只有设置change才能更改最后一次访问时间
                    ((ShiroSession) cached).setChanged(true);
                    // 缓存
                    cache(cached, cached.getId());
                }
            }
            catch(Exception e)
            {
            }
        }
        return cached;
    }
    
    /**
     * 根据会话ID获取会话
     *
     * @param sessionId
     *            会话ID
     * @return ShiroSession
     */
    @Override
    protected Session doReadSession(Serializable sessionId)
    {
        Session session = null;
        try
        {
            
            session = sessionRepository.getSession(sessionId);
            if(session != null)
            {
                // logger.info("sessionId {} ttl {}: ", sessionId, jedisCluster.ttl(key));
                // 重置Redis中缓存过期时间
                ((ShiroSession) session).setChanged(true);
                sessionRepository.refreshSession(sessionId);
                
            }
        }
        catch(Exception e)
        {
        }
        return session;
    }
    
    public Session doReadSessionWithoutExpire(Serializable sessionId)
    {
        Session session = null;
        try
        {
            session = sessionRepository.getSession(sessionId);
        }
        catch(Exception e)
        {
        }
        return session;
    }
    
    /**
     * 如DefaultSessionManager在创建完session后会调用该方法； 如保存到关系数据库/文件系统/NoSQL数据库；即可以实现会话的持久化； 返回会话ID；主要此处返回的ID.equals(session.getId())；
     */
    @Override
    protected Serializable doCreate(Session session)
    {
        // 创建一个Id并设置给Session
        Serializable sessionId = this.generateSessionId(session);
        assignSessionId(session, sessionId);
        try
        {
            // session由Redis缓存失效决定，这里只是简单标识
            // session.setTimeout(seconds);
            
            // jedisCluster.setex(keySerializer.serialize((prefix + sessionId)), seconds
            // , valueSerializer.serialize(session));
            
            sessionRepository.saveSession(session);
            logger.info("sessionId {} name {} 被创建", sessionId, session.getClass().getName());
        }
        catch(Exception e)
        {
            logger.warn("创建Session失败", e);
        }
        return sessionId;
    }
    
    /**
     * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     */
    @Override
    protected void doUpdate(Session session)
    {
        // 如果会话过期/停止 没必要再更新了
        try
        {
            if(session instanceof ValidatingSession && !((ValidatingSession) session).isValid())
            {
                return;
            }
        }
        catch(Exception e)
        {
            logger.error("ValidatingSession error");
        }
        try
        {
            if(session instanceof ShiroSession)
            {
                // 如果没有主要字段(除lastAccessTime以外其他字段)发生改变
                ShiroSession ss = (ShiroSession) session;
                if(!ss.isChanged())
                {
                    return;
                }
                ss.setChanged(false);
                ss.setLastAccessTime(new Date());
                
                sessionRepository.updateSession(session);
                // jedisCluster.setex(keySerializer.serialize((prefix + session.getId())), seconds
                // , valueSerializer.serialize(ss));
                
                // 发送广播
                // jedisUtil.publish("shiro.session.uncache", session.getId());
                logger.debug("sessionId {} name {} 被更新", session.getId(), session.getClass().getName());
            }
            else
            {
                logger.debug("sessionId {} name {} 更新失败", session.getId(), session.getClass().getName());
            }
        }
        catch(Exception e)
        {
            logger.warn("更新Session失败", e);
        }
    }
    
    /**
     * 删除会话；当会话过期/会话停止（如用户退出时）会调用
     */
    @Override
    public void doDelete(Session session)
    {
        logger.debug("begin doDelete {} ", session);
        try
        {
            
            sessionRepository.deleteSession(session.getId());
            
            this.uncache(session.getId());
            logger.debug("shiro session id {} 被删除", session.getId());
        }
        catch(Exception e)
        {
            logger.warn("删除Session失败", e);
        }
    }
    
    /**
     * 删除cache中缓存的Session
     */
    public void uncache(Serializable sessionId)
    {
        try
        {
            Session session = super.getCachedSession(sessionId);
            super.uncache(session);
            logger.debug("删除本地 cache中缓存的Session id {} 的缓存失效", sessionId);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    
    /**
     * 获取当前所有活跃用户，如果用户量多此方法影响性能
     */
    @Override
    public Collection<Session> getActiveSessions()
    {
        
        return Lists.newArrayList();
    }
    
    /**
     * 返回本机Ehcache中Session
     */
    public Collection<Session> getEhCacheActiveSessions()
    {
        return super.getActiveSessions();
    }
    
    public void setSessionRepository(ShiroSessionRepository sessionRepository)
    {
        this.sessionRepository = sessionRepository;
    }
    
}
